<?php

if (!defined('BASEPATH'))
    exit('No direct script access allowed');

/**
 * Extends the form validation functionalities.
 * @package libraries
 * @category Library
 * 
 * @author Mark Angelo Angulo
 * @copyright (c) 2013, makeapps.at
 */
class MY_Form_validation extends CI_form_validation {

    /**
     * Constructor Method
     * @param type $config
     */
    function MY_Form_validation( $config = array() )
    {
        parent::__construct($config);
    }
    
    /**
     * Error Array
     * Returns the error messages as an associative array
     * @access public
     * @return  array
     */
    public function error_assoc()
    {
        if (count($this->_error_array) === 0)
        {
                return FALSE;
        } else
        {
            return $this->_error_array;
        }
    }
    
    /**
     * Error Array
     * Returns the error messages as an array
     * @access public
     * @return  array
     */
    public function error_array()
    {
        if (count($this->_error_array) === 0)
        {
            return array();
        } else
        {
            $errors = array();
            foreach($this->_error_array as $error)
            {
                array_push($errors, trim($error));
            }
            return $errors;
        }
    }
    
    /**
     * Error Array
     * Returns the error messages as an array
     * @access public
     * @return  array
     */
    public function delimited_errors()
    {
        if (count($this->_error_array) === 0)
        {
            return array();
        } else
        {
            $errors = array();
            foreach($this->_error_array as $key => $error)
            {
                $errors[$key] = $this->_error_prefix . trim($error) . $this->_error_suffix;
            }
            return $errors;
        }
    }
    
    /*
     * Add custom error
     * @access public
     * @param $fields Array containing custom errors
     */
    function set_errors($fields)
    {
        if (is_array($fields) and count($fields))
        {
            foreach ($fields as $key => $val)
            {
                $this->_error_array[$key] = $val;
                $this->_field_data[$key]['error'] = $val;
            }
        }
    }
    
    /**
     * Removes all field data
     * @access public
     */
    function unset_field_data()
    {    
        unset($this->_field_data);    
        log_message('debug', "Form Validation Field Data Unset");
    }
    
    /*
     * Valid url characters
     * @access  public
     * @param   $str The string to be validated as a correct URL.
     * @return  boolean Returns true if the string being validated is a valid URL, otherwise returns false.
     */
    public function valid_url($str)
    {
        $this->CI->form_validation->set_message('valid_url', 'The %s field must be a valid URL');
        return ( preg_match("/^(http|https|ftp):\/\/([A-Z0-9][A-Z0-9_-]*(?:\.[A-Z0-9][A-Z0-9_-]*)+):?(\d+)?\/?/i", $str) ) ? TRUE : FALSE;
    }

    /*
     * Valid phone number characters
     * @access  public
     * @param   $str The string to be validated as a correct phone number.
     * @return  boolean Returns true if the string being validated is a valid phone number, otherwise returns false.
     */
    public function valid_phone_chars($str)
    {
        $this->CI->form_validation->set_message('valid_phone_chars', 'The %s field must be a valid phone number');
        return ( preg_match("/^([0-9+\(\).])+$/i", $str) ) ? TRUE : FALSE;
    }

    /*
     * PCI compliance password.
     * Must contain atleast 6 characters, an uppercase and a number.
     * @access  public
     * @param   $str The string to be validated as a correct PCI password.
     * @return  bool Returns true if the string being validated is a valid PCI password, otherwise returns false.
     */
    public function pci_password($str)
    {
        $special = '!@#$%*-_=+.';
        $this->CI->form_validation->set_message('pci_password', 'For PCI compliance, %s must be between 6 and 99 characters in length, must not contain two consecutively repeating characters, contain at least one upper-case letter, at least one lower-case letter, at least one number, and at least one special character (' . $special . ')');
        return (preg_match('/^(?=^.{6,99}$)(?=.*[0-9])(?=.*[A-Z])(?=.*[a-z])(?=.*[' . $special . '])(?!.*?(.)\1{1,})^.*$/', $str)) ? TRUE : FALSE;
    }

    /**
     * Required if another field has a value (related fields) or if a field has a certain value.
     * @access  public
     * @param   string  $str The string to be validated as a required field.
     * @param   string  $field The string that matches the required field.
     * @return  bool Returns true if the string being validated is related in the field being related with, otherwise returns false.
     */
    public function required_if($str, $field)
    {
        list($fld, $val) = explode(',', $field, 2);
        $this->CI->form_validation->set_message('required_if', 'The %s field is required.');
        // $fld is filled out
        if ( isset($_POST[$fld]) )
        {
            // Must have specific value
            if ($val)
            {
                // Not the specific value we are looking for
                if ($_POST[$fld] == $val AND !$str)
                {
                    return FALSE;
                }
            }
            return TRUE;
        }
        return FALSE;
    }

    /**
     * Validates a fraction
     * @access  public
     * @param   string  $str The string to be validated as a correct fraction.
     * @return  bool Returns true if the string being validated is a valid fraction, otherwise returns false.
     */
    public function fraction($str)
    {
        $this->CI->form_validation->set_message('fraction', 'The %s field must be a valid fraction.');
        return ( preg_match("/^(\d++(?! */))? *-? *(?:(\d+) */ *(\d+))?.*$/", $str) ) ? TRUE : FALSE;
    }

    /**
     * Alpha-numeric characters, underscores, and dashes/spaces only.
     * @access  public
     * @param   string  $str The string to be validated as a correct alpha-numeric value.
     * @return  bool returns true if the string being validated is a valid alpha-numeric value, otherwise returns false.
     */
    public function alpha_dash_space($str)
    {
        $this->CI->form_validation->set_message('alpha_dash_space', 'The %s field must contain alpha-numeric characters, underscores, dashes and spaces only');
        return ( preg_match("/^([-a-z0-9_ ])+$/i", $str) ) ? TRUE : FALSE;
    }
    
    /**
     * Date formatted as yyyy-mm-dd
     * @access  public
     * @param   string  $str The string to be validated as a correct date value.
     * @return  bool returns true if the string being validated is a valid date value, otherwise returns false.
     */
    public function valid_dash_date($str)
    {
        $this->CI->form_validation->set_message('valid_dash_date', 'The %s field must contain valid date format yyyy-mm-dd');
        if (ereg("([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})", $str))
        {
            $arr = split("-", $str);     // splitting the array
            $yy = $arr[0];            // first element of the array is year
            $mm = $arr[1];            // second element is month
            $dd = $arr[2];            // third element is days
            return ( checkdate($mm, $dd, $yy) );
        } else
        {
            return FALSE;
        }
    }

    /**
     * Date formatted as mm/dd/yyyy
     * @access  public
     * @param   string  $str The string to be validated as a correct date value.
     * @return  bool returns true if the string being validated is a valid date value, otherwise returns false.
     */
    public function valid_slash_date($str)
    {
        $this->CI->form_validation->set_message('valid_slash_date', 'The %s field must contain valid date format mm/dd/yyyy');
        return ( preg_match("/^(([1-9])|(0[1-9])|(1[0-2]))\/(([0-9])|([0-2][0-9])|(3[0-1]))\/(([0-9][0-9])|([1-2][0,9][0-9][0-9]))$/", $str) ) ? TRUE : FALSE;
    }
    
    /**
     * Date formatted as mm/dd/yyyy hh:mm am/pm
     * @access  public
     * @param   string  $str The string to be validated as a correct date value.
     * @return  bool returns true if the string being validated is a valid date value, otherwise returns false.
     */
    public function valid_slash_datetime($str)
    {
        $this->CI->form_validation->set_message('valid_slash_datetime', 'The %s field must contain valid date format mm/dd/yyyy hh:mm am/pm');
        return ( preg_match("/^(([1-9])|(0[1-9])|(1[0-2]))\/(([0-9])|([0-2][0-9])|(3[0-1]))\/(([0-9][0-9])|([1-2][0,9][0-9][0-9]))\s((0?[1-9]|1[012])(:[0-5]\d){1,2}(\ [AaPp][Mm]))$/", $str) ) ? TRUE : FALSE;
    }
    
    /**
     * Time formatted as hh:mm am/pm
     * @access  public
     * @param   string  $str The string to be validated as a correct time value.
     * @return  bool returns true if the string being validated is a valid time value, otherwise returns false.
     */
    public function valid_time_meridian($str)
    {
        $this->CI->form_validation->set_message('valid_time_meridian', 'The %s field must be contain valid time format hh:mm am/pm.');
        return ( preg_match("/(0?\d|1[0-2])(:(0\d|[0-5]\d))?[ ]?(A|P)M/i", $str) ) ? TRUE : FALSE;
    }
    
    /**
     * Validates hexadecimal color value like #ffffff
     * @access public
     * @param string $str String to be validated as correct hexadecimal color value.
     * @return boolean  Returns true if valide hexadecimal color value, otherwise returns false.
     */
    public function valid_color($str)
    {
        return TRUE;
    }
    
    /**
     * Validates hexadecimal color value like #ffffff
     * @access public
     * @param string $str String to be validated as correct hexadecimal color value.
     * @return boolean  Returns true if valide hexadecimal color value, otherwise returns false.
     */
    public function valid_hex_color($str)
    {
        $this->CI->form_validation->set_message('valid_hex_color', 'The %s field must be contain valid color format #ffffff.');
        return ( preg_match("/^\#?[A-Fa-f0-9]{3}([A-Fa-f0-9]{3})?$/", $str) ) ? TRUE : FALSE;
    }

    
    /**
     * Validates hexadecimal color value like rgb(
     * @access public
     * @param string $str String to be validated as correct hexadecimal color value.
     * @return boolean  Returns true if valide hexadecimal color value, otherwise returns false.
     */

    public function valid_rgb_color($str)
    {
        $this->CI->form_validation->set_message('valid_rgb_color', 'The %s field must be contain valid color format rgb(255,255,255).');
        return ( preg_match("/^rgb\(([01]?\d\d?|2[0-4]\d|25[0-5])\,([01]?\d\d?|2[0-4]\d|25[0-5])\,([01]?\d\d?|2[0-4]\d|25[0-5])\)$/", $str) ) ? TRUE : FALSE;
    }
    
    /**
     * Validates rgba color value like rgba(255,255,255,0.50)
     * @access public
     * @param string $str String to be validated as correct hexadecimal color value.
     * @return boolean  Returns true if valide hexadecimal color value, otherwise returns false.
     */
    public function valid_rgba_color($str)
    {
        $this->CI->form_validation->set_message('valid_rgba_color', 'The %s field must be contain valid color format rgba(255,255,255,0.25).');
        return ( preg_match("/^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+(?:\.\d+)?))?\)$/", $str) ) ? TRUE : FALSE;
    }
    
    
    /**
     * Validates hsl color value like hsl(255,57%,35%)
     * @access public
     * @param string $str String to be validated as correct hexadecimal color value.
     * @return boolean  Returns true if valide hexadecimal color value, otherwise returns false.
     */
    public function valid_hsl_color($str)
    {
        $this->CI->form_validation->set_message('valid_hsl_color', 'The %s field must be contain valid color format hsl(10,10%,10%).');
        return ( preg_match("/^hsl\\(\\s*(0|[1-9]\\d?|[12]\\d\\d|3[0-5]\\d)\\s*,\\s*((0|[1-9]\\d?|100)%)\\s*,\\s*((0|[1-9]\\d?|100)%)\\s*\\)$/", $str) ) ? TRUE : FALSE;
    }
    
    /**
     * Validates hsl color value like hsl(255,57%,35%,0.25)
     * @access public
     * @param string $str String to be validated as correct hexadecimal color value.
     * @return boolean  Returns true if valide hexadecimal color value, otherwise returns false.
     */
    public function valid_hsla_color($str)
    {
        return TRUE;
    }
    
    /**
     * Time formatted as hh:mm am/pm or h:mm am/pm
     * @access  public
     * @param   string  $str The string to be validated as a correct time value.
     * @return  bool returns true if the string being validated is a valid time value, otherwise returns false.
     */
    public function valid_12hr_time($str)
    {
        $this->CI->form_validation->set_message('valid_12hr_time', 'The %s field must be contain valid time format hh:mm am/pm.');
        return ( preg_match("/^((0?[1-9]|1[012])(:[0-5]\d){1,2}(\ [AaPp][Mm]))$/", $str) ) ? TRUE : FALSE;
    }
    
    /**
     * Time formatted as hh:mm:ss am/pm or h:mm:ss am/pm
     * @access  public
     * @param   string  $str The string to be validated as a correct time value.
     * @return  bool returns true if the string being validated is a valid time value, otherwise returns false.
     */
    public function valid_12hr_sec_time($str)
    {
        $this->CI->form_validation->set_message('valid_12hr_sec_time', 'The %s field must be contain valid time format hh:mm:ss am/pm.');
        return ( preg_match("/^((0?[1-9]|1[012])(:[0-5]\d)(:[0-5]\d){1,2}(\ [AaPp][Mm]))$/", $str) ) ? TRUE : FALSE;
    }
    
    /**
     * Time formatted as hh:mm or h:mm
     * @access  public
     * @param   string  $str The string to be validated as a correct time value.
     * @return  bool returns true if the string being validated is a valid time value, otherwise returns false.
     */
    public function valid_24hr_time($str)
    {
        $this->CI->form_validation->set_message('valid_24hr_time', 'The %s field must be contain valid time format hh:mm.');
        return ( preg_match("/(^([0-9]|[0-1][0-9]|[2][0-3]):([0-5][0-9])$)|(^([0-9]|[1][0-9]|[2][0-3])$)/", $str) ) ? TRUE : FALSE;
    }
    
    /**
     * Time formatted as hh:mm:ss or h:mm:ss
     * @access  public
     * @param   string  $str The string to be validated as a correct time value.
     * @return  bool returns true if the string being validated is a valid time value, otherwise returns false.
     */
    public function valid_24hr_sec_time($str)
    {
        $this->CI->form_validation->set_message('valid_24hr_sec_time', 'The %s field must be contain valid time format hh:mm:ss.');
        return ( preg_match("/(^([0-9]|[0-1][0-9])|([2][0-3])):([0-5][0-9]):([0-5][0-9]$)/", $str) ) ? TRUE : FALSE;
    }
    
    /**
     * Valid URLs
     *
     * @access	public
     * @param	string
     * @return	bool
     */
    public function valid_urls($str)
    {
        if (strpos($str, ',') === FALSE)
        {
            return $this->valid_url(trim($str));
        }

        foreach (explode(',', $str) as $url)
        {
            if (trim($url) != '' && $this->valid_url(trim($url)) === FALSE)
            {
                return FALSE;
            }
        }
        
        return TRUE;
    }
    
    /**
     * Valid Numbers
     *
     * @access	public
     * @param	string
     * @return	bool
     */
    public function valid_numbers($str)
    {
        if (strpos($str, ',') === FALSE)
        {
            return is_numeric(trim($str));
        }
        foreach (explode(',', $str) as $num)
        {
            if (trim($num) != '' && is_numeric(trim($num)) === FALSE)
            {
                return FALSE;
            }
        }
        return TRUE;
    }

    /**
     * Checks a single ID or a set of IDs are valid 
     * @access	public
     * @param	array/string An array or a comma separated string containing numbers
     * @return	bool Returns TRUE if all values are valid numbers, otherwise FALSE
     */
    public function valid_ids($arr)
    {
        if(is_array($arr)) {
            foreach ($arr as $id)
            {
                if (trim($id) != '' && is_numeric(trim($id)) === FALSE)
                {
                    return FALSE;
                }
            }
            return TRUE;
        } else
        {
            if (strpos($arr, ',') === FALSE)
            {
                return is_numeric(trim($arr));
            }
            
            foreach (explode(',', $arr) as $num)
            {
                if (trim($num) != '' && is_numeric(trim($num)) === FALSE)
                {
                    return FALSE;
                }
            }
            return TRUE;
        }
    }
    
    /*
     * Checks if a certain record exists on the database
     * @access public
     * @param string $str String containing the item to be checked
     * @parma string $value Database table row to compare the value with
     */
    function exists($str, $value)
    {
        list($table, $column) = explode('.', $value, 2);
        $query = $this->CI->db->query("SELECT COUNT(*) AS count FROM $table WHERE $column = $str");
        $row = $query->row();
        return ($row->count > 0) ? TRUE : FALSE;
    }
    
    /**
     * Checks a single ID or a set of IDs are valid and existing in the database
     * @access	public
     * @param	array/string An array or a comma separated string containing numbers
     * @return	bool Returns TRUE if all values are valid numbers, otherwise FALSE
     */
    public function ids_exist($arr, $value)
    {
        list($table, $column) = explode('.', $value, 2);
        
        // check if array or comma separated string
        if(is_array($arr)) {
            foreach ($arr as $id)
            {
                // check if numeric
                if (trim($id) != '' && is_numeric(trim($id)) === FALSE)
                {
                    return FALSE;
                }
                
                // check if existing
                $query = $this->CI->db->query("SELECT COUNT(*) AS count FROM $table WHERE $column = $id");
                $row = $query->row();
                if( ! ($row->count > 0) )
                {
                    return FALSE;
                }
            }
            return TRUE;
        } else
        {
            // check if numeric
            if (strpos($arr, ',') === FALSE)
            {
                return is_numeric(trim($arr));
            }
            foreach (explode(',', $arr) as $num)
            {
                // check if numeric
                if (trim($num) != '' && is_numeric(trim($num)) === FALSE)
                {
                    return FALSE;
                }
                // check if existing
                $query = $this->CI->db->query("SELECT COUNT(*) AS count FROM $table WHERE $column = $num");
                $row = $query->row();
                if( ! ($row->count > 0) )
                {
                    return FALSE;
                }
            }
            return TRUE;
        }
    }
    
    // ------------------------------------------------------------------------- CMS VALIDATIONS
    
    /**
     * Validates time formatted as hh:mm am/pm
     * @access  public
     * @param   string  $str The string to be validated as a correct time value.
     * @return  bool returns true if the string being validated is a valid time value, otherwise returns false.
     */
    public function valid_time($str)
    {
        $this->CI->form_validation->set_message('valid_time_meridian', 'The %s field must be contain valid time format hh:mm am/pm.');
        return ( preg_match("/(0?\d|1[0-2])(:(0\d|[0-5]\d))?[ ]?(A|P)M/i", $str) ) ? TRUE : FALSE;
    }
    
    public function valid_date($str)
    {
        return TRUE;
    }
    
    public function valid_datetime($str)
    {
        return TRUE;
    }
    
    /**
     * Valid Captcha
     * validate captcha images
     * @param type $str String to be validated
     * @return boolean Returns true if valid captcha, otherwise returns false.
     */
    public function valid_captcha($str)
    {
        $captcha = $this->CI->session->flashdata('captcha');
        $expiration = time() - 900; // 15 minutes
        
        if( isset($captcha) && $captcha['time'] > $expiration)
        {
            if( $str === $captcha['word'] )
            {
                if( $this->CI->session->userdata('ip_address') == $this->CI->input->ip_address() )
                {
                    return TRUE;
                } else
                {
                    $this->CI->form_validation->set_message('valid_captcha', 'Captcha expired.');
                    return FALSE;
                }
            } else
            {
                $this->CI->form_validation->set_message('valid_captcha', 'The %s field doesn\'t match.');
                return FALSE;
            }
        } else
        {
            $this->CI->form_validation->set_message('valid_captcha', 'Captcha expired.');
            return FALSE;
        }
    }
    
}

